package tetris.agent;

import tetris.simulator.State;
import tetris.features.FeatureExtraction;
import java.util.*;


public class ApproxFunction {
	
	public double wtdLinearFeatures(State s, int action, double[] weight) 
	{	
		State newState = new State(s);
		newState.makeMove(action);

		double[] allFeatures = getFeatures(newState);

		// wt.features (dot product)
		double Q = dotProduct(allFeatures, weight);

		return Q;
	}


	public double[] delLogPolicy(State s,int[][] legalMoves, int action, double[] weight){
				
		State newState = new State(s);
		newState.makeMove(action);
		
		double[] fsa = getFeatures(newState);
		
		double[] delLog_term2 = new double[fsa.length];
		for (int i = 0; i< legalMoves.length; i++){
			State sClone = new State(s);
			double policysa_new = softMaxPolicy(sClone, legalMoves, i, weight);
			
			sClone.makeMove(i);
			double[] fsa_new = getFeatures(sClone);
			
			delLog_term2 = sumArrays(delLog_term2, scalarProduct(fsa_new,policysa_new),true);
		}
		
		double[] delLog = sumArrays(fsa, delLog_term2, false);
		return delLog;
	}

	public double softMaxPolicy(State s,int[][] legalMoves, int action, double[] weight ){
		// Wtd Linear Fts for Policy
		double policyFts = wtdLinearFeatures(s, action, weight);

		// Normalization
		double Z = 0;
		for (int i = 0; i< legalMoves.length; i++){
			Z += Math.exp(wtdLinearFeatures(s, i, weight));
		}

		// Policy
		double policy = Math.exp(policyFts)/Z;
		return policy;
	}



	public double[] getFeatures(State s){

		FeatureExtraction features = new FeatureExtraction();

		//get features
		double[] height = features.top(s);
		double[] heightDiff = features.heightDifference(s);
		double heightMax = features.maxtop(s);
		double[] holes = features.holes(s);

		// concatenate all features into a vector/array
		double[] allFeatures = new double[height.length+heightDiff.length];
		System.arraycopy(height, 0, allFeatures, 0, height.length);
		System.arraycopy(heightDiff, 0, allFeatures, allFeatures.length, heightDiff.length);
		System.arraycopy(holes, 0, allFeatures, allFeatures.length, holes.length);
		System.arraycopy(heightMax, 0, allFeatures, allFeatures.length, 1);

		return allFeatures;
	}



    // Utility Functions
	public double dotProduct(double[] fts, double[] wt){
		double dtPrd = 0.0;
		for (int i = 0; i < wt.length; i++) {
			dtPrd += fts[i] * wt[i];
		}
		return dtPrd;
	}
	
	public double[] scalarProduct(double[] Matrix, double K){
		double KPrd[] =  new double[Matrix.length];
		for (int i = 0; i < Matrix.length; i++) {
			KPrd[i]= K * Matrix[i];
		}
		return KPrd;
	}
	
	public double[] sumArrays(double[] Matrix1, double[] Matrix2, boolean operator){
		double[] sumArr = new double[Matrix1.length];
		
		if (operator==true){
			for (int i = 0; i < Matrix1.length; i++) {
				sumArr[i]=  Matrix1[i] + Matrix2[i];
			}
		}
		else{
			for (int i = 0; i < Matrix1.length; i++) {
				sumArr[i]=  Matrix1[i] - Matrix2[i];
			}
		}
		return sumArr;
	}

}
